查看原文
其他

Unity着色器教程 | 积雪效果

1970-01-01 Unity官方 Unity官方平台

为游戏中的所有纹理都加上雪花可能需要花费大量时间。本文将展示在Unity中如何创建Image Effect(屏幕空间着色器)来快速改变场景的季节。


请点击【阅读原文】下载着色器,并查看本文涉及的所有源码。使用着色器前后效果图对比如下:



工作原理

上面两张图显示的是同一个场景。它们之间唯一的区别就是第二张图启用了相机上的雪花特效。实现该效果无需对场景的贴图做任何更改。这是什么原理呢?

其实原理非常简单。就是假定所有法线朝上的像素点(如:草,地板等)都需要覆盖雪花。同样,法线朝着其它方向的像素点(如:松树,墙),则需要在雪花纹理和原始纹理之间进行平缓过渡。

获取所需数据

实现上面的雪花效果有以下准备事项:

  • 将渲染路径设置为Deferred(延迟渲染)

  • 将Camera.depthTextureMode设置为DepthNormals


由于第二项可以很方便地由屏幕特效脚本进行设置,所以如果游戏已经使用了前向渲染路径(Forward Rendering Path)时,第一项很容易出问题。

将Camera.depthTextureMode设置为DepthNormals后可以读取屏幕深度(像素与相机之间的距离)和法线(所朝的方向)。

创建一个屏幕特效(Image Effect)由至少一个脚本和一个着色器构成。通常这个着色器不是用来渲染3D物体的,而是根据给定的输入数据渲染一个全屏的图像。在本文的例子中,输入数据就是一张相机渲染的结果图片以及一些用户设置的属性。

这里只是基础的设置,还不能生成雪花。有趣的事情还在后面。

着色器

雪花着色器是无光照着色器(unlit shader),因为屏幕空间是没有光照的,所以也不会用到任何光照信息。片段着色器才是重要部分,通过ScreenSpaceSnow脚本来获取所有数据。脚本代码请点击【阅读原文】查看。


找出需要下雪的地方

正如之前所说,所有法线朝上的表面都将覆盖雪。相机已经设置了生成深度法线贴图,所以现在直接获取即可。


查看Unity官方文档可以了解_CameraDepthNormalsTexture的意义:

深度贴图可以作为一个着色器的全局着色器属性进行采样。通过声明名为_CameraDepthTexture的采样器,就能够采样相机的主深度纹理。
_CameraDepthTexture总是引用相机的主深度贴图。


Unity文档解释深度和法线的数据都打包为16位。所以获取法线需要调用DecodeDepthNormal方法进行解包。这个方法检索的是相机空间的法线。也就是说,如果旋转屏幕相机,那么法线朝向也会改变。脚本中将法线乘以_CamToWorld 矩阵就是为了避免这种情况。它会将法线从相机空间转换为世界空间,这样就不再依赖于相机的透视。


暂时渲染为RGB图像。在Unity中,Y轴是默认向上的。图中绿色部分表示Y坐标轴的值。目前为止结果不错!接下来配置积雪覆盖区域顶部和底部的阀值,以便于调整场景的积雪量。




雪纹理

如果没有纹理,雪看起来会不真实。最难的部分就是将2D纹理(屏幕空间)应用到3D物体上。一种方法是获取像素的世界坐标,然后将世界坐标的X和Z值作为纹理坐标。

这里涉及到一些数学知识,您只需知道vpos是视口坐标,wpos是由视口坐标与_CamToWorld矩阵相乘而得到的世界坐标,并且它通过除以远平面的位置(_ProjectionParams.z)来转换为有效的世界坐标。最后使用XZ坐标乘以可配置参数_SnowTexScale和远平面,来计算雪的颜色并获取适当的值。



合并

下面将积雪与场景进行合并。获取场景原始颜色,并使用snowAmount进行插值渐变为snowColor 。



最后一步:将_TopThreshold设为0.6:


完成!

结论

全屏效果见下图。看起来不错吧?



点击【阅读原文】可查看文中涉及的所有代码,了解教程详细步骤,并下载积雪着色器,您也可以将其用于您的项目!    


本文来源于:theknightsofunity.com


相关阅读

Unity小技巧:粒子系统性能-剔除

Unity 2D教程 | 相机系统

Unity 2D教程 | 平台游戏

Unity教程|3D无尽跑酷游戏

Unity教程|制作一款3D射击游戏


点击“阅读原文”下载着色器!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存